Skip to content

THX#3

Open
y13sint wants to merge 24 commits into
ForgetMeAI:mainfrom
y13sint:main
Open

THX#3
y13sint wants to merge 24 commits into
ForgetMeAI:mainfrom
y13sint:main

Conversation

@y13sint

@y13sint y13sint commented Jun 10, 2026

Copy link
Copy Markdown

X

y13sint and others added 24 commits June 6, 2026 09:11
createChatV2() and sendMessage() each pulled a token via round-robin getAvailableToken(), so with 2+ accounts the chat was created under one account and the message sent under another, making Qwen reply 'chat is not exist'. Resolve the token once in sendMessage and pass it into createChatV2; reset chatId on 401/429 retries so a fresh chat is created under the new account.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The default model was hardcoded as 'qwen-max-latest' in config and in 22 places across routes.js; Qwen now rejects it with 'Model not found', so any request without an explicit model failed. Route all fallbacks through the existing DEFAULT_MODEL config value and update its default to a current model (qwen3.7-max).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a self-contained dashboard at GET / and /dashboard: server status, 'how to connect' (base URL/key/model), account management (add token / delete / status with expiry), model list and a quick chat test. No external dependencies тАФ a single static HTML file.

Account management is exposed via REST (GET/POST/DELETE /api/accounts), gated to localhost only with an Origin check on mutating requests (the proxy serves Access-Control-Allow-Origin: *). deleteAccount() validates the id against ^acc_[a-zA-Z0-9]+$ and confirms the resolved path stays inside the accounts dir, preventing path traversal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…own, file attachments, download proxy

Major dashboard upgrade: tabbed layout (Chat-first), Overview merges connection info + models + account management. Account ops: live check, rate-limit/expiry display, relogin (token update), add/delete тАФ backed by new POST /api/accounts/:id/check and /:id/update. Image & video generation tabs with download. Chat: streaming, multi-turn, markdown (marked+DOMPurify via CDN with SRI), image/document attachments via /api/files/upload. New GET /api/download proxy with strict Qwen/Aliyun CDN whitelist and SSRF-safe manual redirect validation. Self-contained HTML; localhost-gated account routes with CSRF Origin checks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix multi-account chat (token desync) and broken default model
List every supported env var (server, limits, timeouts, paths, browser, logging, image generation) with defaults and comments, and add a Configuration section in README linking to it.

Variables are read from process.env directly (no .env autoloader); the file documents shell/docker-compose configuration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract the hardcoded 24-hour rate-limit fallback into a named RATE_LIMIT_HOURS constant (config.js), sourced from QWEN_RATELIMIT_HOURS with a 24 default. Use it in chat.js (429 handling) and the markRateLimited default in tokenManager.js.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The account-list endpoint was guarded only by an IP check (localOnly). Combined with the global Access-Control-Allow-Origin: * header, a malicious page could cross-origin fetch http://localhost:3264/api/accounts from the victim's browser and read the pool (ids, statuses, token previews, labels).

Add the existing sameOriginOnly guard (already used on the mutating account routes) so cross-origin reads are rejected; the same-origin dashboard and non-browser clients (no Origin/Referer) are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The getHistoryFilePath() function used unsanitized chatId values directly
in path.join(), allowing an attacker to read, write, or delete arbitrary
.json files on the filesystem via directory traversal sequences in the
chatId parameter (e.g., "../../etc/passwd").

This adds a sanitizeChatId() function that:
- Rejects values containing path separators (/ or \) or traversal (..)
- Whitelists only [a-zA-Z0-9_-] characters
- Adds defense-in-depth resolved-path containment check

All callers of getHistoryFilePath (saveHistory, loadHistory, chatExists,
deleteChat) are protected since sanitization is applied at the single
chokepoint function. The chatExists function now wraps in try/catch to
gracefully handle invalid IDs.
feat: make rate-limit cooldown hours configurable via env
docs: add .env.example documenting all environment variables
…tize-fa04

fix(chatHistory): sanitize chatId to prevent path traversal (CWE-22)
sameOriginOnly 403'd the account-import popup, whose POST to /api/accounts
carries a chrome-extension:// (or moz-extension://) Origin — so one-click
import never worked. Web pages cannot forge an extension Origin and the
extension declares an explicit host permission, so these are trusted;
malicious http(s) origins are still rejected.
Add built-in dashboard: chat (markdown+files), media generation, account management
…aming

For streaming requests the server first tries a browserless node fetch against
Qwen's API. Qwen's Aliyun WAF sometimes challenges that fetch and returns a
non-SSE captcha HTML page with a 200 status. The previous code treated any
non-empty response as returnable, so the captcha page surfaced to the client
as the model's answer.

Detect the WAF case (success !== true and error matches "Unexpected non-SSE
200") and exclude it from canReturnDirectly, so execution falls through to the
existing browser (page.evaluate) path, which carries the real logged-in
session and isn't challenged.
…ing Chrome) auth modes

Qwen blocks headless automation — CDP evaluate can hang and first login can hit
a one-time verification. Two opt-in env vars make initial auth reliable:

- QWEN_VISIBLE=1 runs the browser headed so the page renders normally and any
  one-time verification can be passed; the session then persists.
- QWEN_CDP_URL attaches (puppeteer.connect) to your already-running, logged-in
  Chrome started with --remote-debugging-port. It reuses a human-trusted
  session, so Qwen serves no captcha and the headless evaluate no longer hangs.
  In CDP mode a fresh tab is opened (the user's current tab is left alone) and
  the interactive empty-browser login flow is skipped.

Both are off by default; without them behaviour is unchanged.
Lets each Qwen account carry an optional human-readable label so multiple
accounts in the pool are easy to tell apart in the dashboard.

- tokenManager: setLabel(id, label) (trimmed, 60-char cap, blank clears the
  label); addTokenFromString accepts an optional label.
- routes: GET /accounts returns the label; POST /accounts/:id/label sets it,
  behind the existing localOnly + sameOriginOnly guards.
- dashboard: a label chip on each account row plus an inline "ярлык" editor,
  wired through the existing delegated click handler and styled with the
  dashboard's own tokens.

Fully optional and backward-compatible — accounts without a label render as
before.
fix(chat): fall back to browser path when Aliyun WAF blocks node-streaming
feat(browser): QWEN_VISIBLE (headed) and QWEN_CDP_URL (attach to running Chrome) auth modes
feat(accounts): human-readable account labels
Updated the description and added new features related to Qwen Chat.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants